--- 旧版Redisson（部分）释放锁Lua脚本
--[[--- 释放锁的Lua脚本
local key = KEYS[1]; -- 锁的key
local threadId = ARGV[1]; -- 线程唯一标识
local releaseTime = ARGV[2]; -- 锁的自动释放时间
-- 判断当前锁是否还是被自己持有
if (redis.call('HEXISTS', key, threadId) == 0) then
    return nil; -- 如果已经不是自己，则直接返回
end;
-- 是自己的锁，则重入次数-1
local count = redis.call('HINCRBY', key, threadId, -1);
-- 判断是否重入次数是否已经为0
if (count > 0) then
    -- 大于0说明不能释放锁，重置有效期然后返回
    redis.call('EXPIRE', key, releaseTime);
    return nil;
else  -- 等于0说明可以释放锁，直接删除
    redis.call('DEL', key);
    return nil;
end;]]


--- 新版Redisson释放锁Lua脚本
local lockKey = KEYS[1];    -- 锁的key
local channelName = KEYS[2];    -- 频道名称
local unlockLatchNameByRequestId = KEYS[3]; -- 释放锁请求id
local unlockMessage = ARGV[1];  -- 释放锁消息
local leaseTime = ARGV[2];  -- 锁的有效期
local threadId = ARGV[3];   -- 线程唯一标识
local publishCommand = ARGV[4]; -- 发布消息命令
local timeout = ARGV[5];    -- 超时时间

-- 获取解锁请求标志位的值
local val = redis.call('get', unlockLatchNameByRequestId);
-- 如果标志位存在且不为false，直接返回其数值
if val ~= false then
    return tonumber(val);
end;
-- 检查锁是否是当前线程所属的
if (redis.call('hexists', lockKey, threadId) == 0) then
    -- 如果锁不属于当前线程，则不能释放其他线程的锁，返回nil
    return nil;
end;
-- 在释放锁之前，先将当前线程锁的重入计数器减一
local counter = redis.call('hincrby', lockKey, threadId, -1);
-- 再判断计数器是否大于0，如果重入次数大于0，说明锁的业务不在最外层，因此不能释放锁
if (counter > 0) then
    -- 重置锁的过期时间
    redis.call('pexpire', lockKey, leaseTime);
    -- 设置释放锁请求标志位为0，并设置超时时间
    redis.call('set', unlockLatchNameByRequestId, 0, 'px', timeout);
    -- 释放锁失败，仅将重入计数器-1后返回0
    return 0;
else    -- 如果重入计数器等于0，说明该锁的最外层业务已执行完，可以释放锁
    -- 删除锁
    redis.call('del', lockKey);
    -- 向释放锁的频道发布锁已释放的消息，通知每一个订阅该频道的消费者
    redis.call(publishCommand, channelName, unlockMessage);
    -- 设置释放锁请求标志位为1，并设置过期时间
    redis.call('set', unlockLatchNameByRequestId, 1, 'px', timeout);
    -- 释放锁成功，返回1
    return 1;
end;
